#include "csgprimitivenode.h"
#include "GL/glu.h"

Interval domain_x=::numeric::Hull(0, 1);
Variable CSGPrimitiveNode::x("x", domain_x);
Expression CSGPrimitiveNode::expr_x = x.getSymbol();
Interval domain_y=::numeric::Hull(0, 1);
Variable CSGPrimitiveNode::y("y", domain_y);
Expression CSGPrimitiveNode::expr_y = y.getSymbol();
Interval domain_z=::numeric::Hull(0, 1);
Variable CSGPrimitiveNode::z("z", domain_z);
Expression CSGPrimitiveNode::expr_z = z.getSymbol();

const double threshold = 0.05;

CSGPrimitiveNode::CSGPrimitiveNode()
    : CSGNode(0)
{
}

CSGPrimitiveNode::CSGPrimitiveNode(Expression& e, CSGNode *parent)
    : CSGNode(0, parent)
{
    orgExpr = e;
    expr = e;

    // genPolynomialFamily();
}

CSGPrimitiveNode::~CSGPrimitiveNode()
{
}

void CSGPrimitiveNode::setExpression(Expression& e)
{
    expr = e;
}

double CSGPrimitiveNode::eval(double x, double y, double z)
{
    expression::sym_mapper<double>::type var_map;

    var_map.insert(std::pair<Expression, double>(expr_x, x));
    var_map.insert(std::pair<Expression, double>(expr_y, y));
    var_map.insert(std::pair<Expression, double>(expr_z, z));

    return ::expression::Eval<double>(expr, var_map);
}

bool CSGPrimitiveNode::emptyTest(StarTestBox *box)
{

}

bool CSGPrimitiveNode::starTest(StarTestBox *box, double *pt)
{

}

bool CSGPrimitiveNode::complementStarTest(StarTestBox *box, double *pt)
{

    return box->isStar();
}

bool CSGPrimitiveNode::ptInSet(double *pt)
{
//    Box box(pt[0], pt[0], pt[1], pt[1]);
//    return boxContainsZero(&box, expr);

    expression::sym_mapper<double>::type var_map;

    var_map.insert(std::pair<Expression, double>(expr_x, pt[0]));
    var_map.insert(std::pair<Expression, double>(expr_y, pt[1]));
    var_map.insert(std::pair<Expression, double>(expr_z, pt[2]));

    return ::expression::Eval<double>(expr, var_map) <= 0;
}

void CSGPrimitiveNode::setRotation(double _rot)
{
    _rot = _rot*PI/180;
    Expression expandedExpr = expr.expand();
    int M = expandedExpr.degree(expr_x);
    int N = expandedExpr.degree(expr_y);
    int num = (M+1) * (N+1);

    double **c = new double*[num];

    for (int i = 0; i < num; i++)
    {
        c[i] = new double;
    }

    getCoeff(expandedExpr, M, N, c);

    expr = 0;

    for (int i = 0; i <= M; i++)
    {
        for (int j = 0; j <= N; j++)
        {
            expr += c[i * (N+1) + j][0]
                    * expression::Pow(expr_x * cos(_rot) + expr_y * sin(_rot), i)
                    * expression::Pow(-expr_x * sin(_rot) + expr_y * cos(_rot), j);
        }
    }

    for (int i = 0; i < num; i++)
    {
        delete c[i];
    }
    delete []c;

    if (emptyTestTree)
    {
        StarTestBox *box = emptyTestTree->getRoot()->getData();
        initial(*box);
    }
}

void CSGPrimitiveNode::setTranslation(double x, double y)
{
    Expression expandedExpr = expr.expand();
    int M = expandedExpr.degree(expr_x);
    int N = expandedExpr.degree(expr_y);
    int num = (M+1) * (N+1);

    double **c = new double*[num];

    for (int i = 0; i < num; i++)
    {
        c[i] = new double;
    }

    getCoeff(expandedExpr, M, N, c);

    expr = 0;

    for (int i = 0; i <= M; i++)
    {
        for (int j = 0; j <= N; j++)
        {
            expr += c[i * (N+1) + j][0]
                    * expression::Pow(expr_x - x, i)
                    * expression::Pow(expr_y - y, j);
        }
    }

    for (int i = 0; i < num; i++)
    {
        delete c[i];
    }
    delete []c;

    if (emptyTestTree)
    {
        StarTestBox *box = emptyTestTree->getRoot()->getData();
        initial(*box);
    }
}

void CSGPrimitiveNode::setScale(double x, double y)
{
    Expression expandedExpr = expr.expand();
    int M = expandedExpr.degree(expr_x);
    int N = expandedExpr.degree(expr_y);
    int num = (M+1) * (N+1);

    double **c = new double*[num];

    for (int i = 0; i < num; i++)
    {
        c[i] = new double;
    }

    getCoeff(expandedExpr, M, N, c);

    expr = 0;

    for (int i = 0; i <= M; i++)
    {
        for (int j = 0; j <= N; j++)
        {
            expr += c[i * (N+1) + j][0]
                    * expression::Pow(1/x * expr_x, i)
                    * expression::Pow(1/y * expr_y, j);
        }
    }

    for (int i = 0; i < num; i++)
    {
        delete c[i];
    }
    delete []c;

    if (emptyTestTree)
    {
        StarTestBox *box = emptyTestTree->getRoot()->getData();
        initial(*box);
    }
}

void CSGPrimitiveNode::getCoeff(Expression &expr, int M, int N, double **c)
{
    Expression tempExpr;
    expression::sym_mapper<double>::type var_map;
    var_map.insert(std::pair<Expression, double>(expr_x, 0.0));
    var_map.insert(std::pair<Expression, double>(expr_y, 0.0));

    for (int i = 0; i <= M; i++)
    {
        for (int j = 0; j <= N; j++)
        {
            if (i == 0)
            {
                tempExpr = expr;
            }
            else
            {
                tempExpr = expr.coeff(expression::Pow(expr_x, i));
            }
            if (j != 0)
            {
                tempExpr = tempExpr.coeff(expression::Pow(expr_y, j));
            }
            c[i * (N+1) + j][0] = expression::Eval<double>(tempExpr, var_map);
        }
    }
}

void CSGPrimitiveNode::getCoeff3(Expression &expr, int L, int M, int N, double *c)
{
    Expression tempExpr;
    expression::sym_mapper<double>::type var_map;
    var_map.insert(std::pair<Expression, double>(expr_x, 0.0));
    var_map.insert(std::pair<Expression, double>(expr_y, 0.0));
    var_map.insert(std::pair<Expression, double>(expr_z, 0.0));

    for (int i = 0; i <= L; i++)
    {
        for (int j = 0; j <= M; j++)
        {
            for (int k = 0; k <= N; k++)
            {
                if (i == 0)
                {
                    tempExpr = expr;
                }
                else
                {
                    tempExpr = expr.coeff(expression::Pow(expr_x, i));
                }

                if (j != 0)
                {
                    tempExpr = tempExpr.coeff(expression::Pow(expr_y, j));
                }

                if (k != 0)
                {
                    tempExpr = tempExpr.coeff(expression::Pow(expr_z, k));
                }

                c[i * (M+1) * (N+1) + j * (N+1) + k] = expression::Eval<double>(tempExpr, var_map);
            }
        }
    }
}
